﻿using System;
using System.Threading;
using System.Threading.Tasks;
using AutoMapper;
using K9Nano.Domain;

namespace K9Nano.Caching.EntityCaching
{
    public abstract class EntityCacheBase<TEntity, TKey, TEntityCacheItem> : IEntityCache<TEntity, TKey, TEntityCacheItem>
        where TEntity : class, IEntity<TKey> where TKey : IEquatable<TKey>
        where TEntityCacheItem : class
    {
        protected readonly IRepository<TEntity, TKey> Repository;
        protected readonly IMapper ObjectMapper;
        protected readonly ITypedCache<TEntityCacheItem> Cache;

        protected EntityCacheBase(IUnitOfWork uow, IMapper objectMapper, ITypedCache<TEntityCacheItem> cache)
        {
            Repository = uow.Repository<TEntity, TKey>();
            ObjectMapper = objectMapper;
            Cache = cache;
        }

        protected virtual string GetCacheKey(TKey key) => key.ToString();

        public virtual TEntityCacheItem Get(TKey key)
        {
            var cacheKey = GetCacheKey(key);
            var result = Cache.GetOrAdd(cacheKey, k =>
            {
                var entity = Repository.Get(key);
                var cacheItem = MapToCacheItem(entity);
                return cacheItem;
            });
            return result;
        }

        protected virtual TEntityCacheItem MapToCacheItem(TEntity entity)
        {
            if (entity == null) return null;
            return ObjectMapper.Map<TEntityCacheItem>(entity);
        }
    }

    public abstract class EntityCacheBase<TEntity, TEntityCacheItem> : EntityCacheBase<TEntity, long, TEntityCacheItem>, IEntityCache<TEntity, TEntityCacheItem>
        where TEntity : class, IEntity<long>
        where TEntityCacheItem : class
    {
        protected EntityCacheBase(IUnitOfWork uow, IMapper objectMapper, ITypedCache<TEntityCacheItem> cache) : base(uow, objectMapper, cache)
        {
        }
    }
}